package downstream

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/status"
	"mall/api/mall"
	"mall/api/prod"
	"mall/internal/conf"
	"mall/internal/constant"
	"mall/pkg/ecode"
	"mall/pkg/log"
	"mall/pkg/network"
	"mall/pkg/request_key"
	"time"
)

func newProdClient(ctx context.Context, addr string) (
	client prod.ProdClient, err error) {
	var conn *grpc.ClientConn
	conn, err = grpc.Dial(addr, grpc.WithInsecure(),
		grpc.WithUnaryInterceptor(network.ClientInterceptor(
			"bilin_prod", time.Second*3, prodErrorTranslator)))
	if err != nil {
		err = ecode.AppendMessage(constant.ErrRemoteError, "商品中心服务地址无法建立有效连接, err=%s", err.Error())
		log.FromContext(ctx).Warnw(err.Error())
		return
	}
	client = prod.NewProdClient(conn)
	return
}

func prodErrorTranslator(err error) (errCode *ecode.ErrorCode) {
	rpcStatus := status.Convert(err)
	for _, detail := range rpcStatus.Details() {
		switch t := detail.(type) {
		case *prod.DefaultResp:
			errCode = ecode.NewError(t.ErrorCode, t.ErrorMessage, ecode.ErrorType{
				GRPCCode: rpcStatus.Code(),
				ErrType:  t.ErrorType,
			})
			return
		}
	}
	errCode = ecode.AppendMessage(
		constant.ErrRemoteError, "商品中心服务系统错误，请稍后再试")
	return
}

func (d *Downstream) SelectionProdList(ctx context.Context, req *mall.QueryProdsExcludeShelfRequest,
	existProdIds []uint64) (resp *prod.GetProdListResponse, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if resp, err = d.prodClient.GetProdList(clientCtx, &prod.GetProdListRequest{
		Token: &prod.WebToken{
			EmpId:     req.Token.EmpId,
			SgId:      req.Token.SgId,
			EmpName:   req.Token.EmpName,
			AccountId: req.Token.AccountId,
		},
		PStoreId:        req.PStoreId,
		PageNo:          req.PageNo,
		PageSize:        req.PageSize,
		ProdTypeId:      req.ProdTypeId,
		Query:           req.Query,
		ExcludedProdIds: existProdIds,
	}); err != nil {
		return
	}
	return
}

func (d *Downstream) SelectionMenuProdList(ctx context.Context, req *mall.QueryProdsExcludeShelfRequest,
	existProdIds []uint64) (resp *prod.GetProdListResponse, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if resp, err = d.prodClient.SelectionMenuProdList(clientCtx, &prod.SelectionMenuProdListRequest{
		SgId:        req.Token.SgId,
		PStoreId:    req.PStoreId,
		ProdIds:     existProdIds,
		QueryTypeId: req.ProdTypeId,
		Query:       req.Query,
		PageNo:      req.PageNo,
		PageSize:    req.PageSize,
	}); err != nil {
		return
	}
	return
}

func (d *Downstream) SelectionProdsByProdIds(ctx context.Context, sgId uint32, prodIds []uint64,
	needStorePrice bool) (resp *prod.SelectionProdsFullResponse, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if resp, err = d.prodClient.SelectionProdsFull(clientCtx, &prod.SelectionProdListRequest{
		SgId:           sgId,
		Ids:            prodIds,
		NeedStorePrice: needStorePrice,
	}); err != nil {
		return
	}
	return
}

func (d *Downstream) SelectionMenuProdsByProdIds(ctx context.Context, sgId, pStoreId uint32, prodIds []uint64) (
	prods []*prod.SelectionProdView, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	var resp *prod.SelectionProdsFullResponse
	if resp, err = d.prodClient.SelectionMenuProdsFull(clientCtx, &prod.SelectionMenuProdFullRequest{
		SgId:     sgId,
		PStoreId: pStoreId,
		Ids:      prodIds,
	}); err != nil {
		return
	}
	prods = resp.Prods
	return
}

func (d *Downstream) GetProdInfo(ctx context.Context, sgId, pStoreId uint32, prodId uint64, skipCheck bool) (
	resp *prod.ProdView, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	if resp, err = d.prodClient.SelectionProdInfo(clientCtx, &prod.SelectionProdInfoRequest{
		SgId:      sgId,
		PStoreId:  pStoreId,
		ProdId:    prodId,
		SkipCheck: skipCheck,
	}); err != nil {
		return
	}
	return
}

func (d *Downstream) SelectionProdTypes(ctx context.Context, token *mall.WebToken, typeIds []uint32) (
	types []*prod.SimpleProdTypeView, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	var resp *prod.SelectionProdTypesResponse
	if resp, err = d.prodClient.SelectionProdTypes(clientCtx, &prod.SelectionProdTypesRequest{
		Token: &prod.WebToken{
			EmpId:     token.EmpId,
			SgId:      token.SgId,
			EmpName:   token.EmpName,
			AccountId: token.AccountId,
		},
		Ids: typeIds,
	}); err != nil {
		return
	}
	types = resp.ProdTypes
	return
}

func (d *Downstream) CheckCanModifyPrice(ctx context.Context, token *mall.WebToken, storeId uint32) (
	canModifyPrice bool, err error) {
	if d.prodClient == nil {
		if d.prodClient, err = newProdClient(
			ctx, conf.Conf.Downstream.ProdAddress); err != nil {
			return
		}
	}
	clientCtx, cancel := request_key.NewOutGoingContext(ctx, time.Second*3)
	defer cancel()
	var resp *prod.CheckStoreMenuPropertyResp
	if resp, err = d.prodClient.CheckStoreMenuProperty(clientCtx, &prod.CommonRequest{
		Token: &prod.WebToken{
			EmpId:     token.EmpId,
			SgId:      token.SgId,
			EmpName:   token.EmpName,
			AccountId: token.AccountId,
		},
		PStoreId: storeId,
	}); err != nil {
		return
	}
	canModifyPrice = resp.CanModifyPrice
	return
}
